/* $Header: run_state.c,v 2.2 01/10/02 10:32:34 przybyls Exp $ */

/***********************************************************************/
/*
** Copyright Hewlett-Packard Co. 1991-1993
**
** MODULE NAME:  run_state.c
** ============
**
** Author:  Rick Wikfors (changes by John DeGood)
**
** MODULE PURPOSE:  This module contains the functions necessary for
**                  Run Control.
**
**    IQ Run_State Definitions
**
**       RUN_IDLE
**       PRE_RUN
**       RUN_ACTIVE
**       POST_RUN
**
**    A Boolean variable Scc_Run indicates if the current Run_State
**    is part of a column compensation run.
*/
/***********************************************************************/

/*****************************/
/*   GLOBAL INCLUDE FILES    */
/*****************************/

#include <typedefs.h>
#include <p_wkfile.h>
#include <wkfile.h>
#include <zone_config.h>
#include <config.h>
#include <ramps.h>
#include <zone_status.h>
#include <status.h>
#include <error.h>
#include <sig_types.h>
#include <sigpath.h>
#include <i_op.h>
#include <iomap.h>
#include <leds.h>
#include <regs.h>
#include <run_ui.h>
#include <readiness.h>
#include <sequence.h>
#include <seq_ui.h>
#include <exception.h>
#include <err_handler.h>

/*****************************/
/*   LOCAL INCLUDE FILES     */
/*****************************/

/*****************************/
/*   GLOBAL VARIABLES        */
/*****************************/

INT32 Last_Run_Time;

/*****************************/
/*   EXTERNAL VARIABLES      */
/*****************************/

extern void InitializeSccRun(void);
#if 0
extern MV_FLAG_TYPE MVPending;
#endif

/*****************************/
/*   LOCAL DEFINES           */
/*****************************/

/*****************************/
/*   STATIC PROTOTYPES       */
/*****************************/

#pragma SECTION PROG=SysCode
static void RunActive( void );
#pragma SECTION UNDO

static void RunIdle ( void );
static void PreRun ( void );
static void PostRun ( void );

/*****************************/
/*   STATIC VARIABLES        */
/*****************************/

static BIT32 Prepare_Seen_During_Post_Run;

/*****************************/
/*   IMPLEMENT SECTION       */
/*****************************/

/***********************************************************************/
/*
** FUNCTION:  InitializeRunControl
** ========
**
** PURPOSE:  This function initializes all of the necessary variables
**           for the run state machine.  Called at poweron.
**
** RETURNED VALUE:  None.
*/
/***********************************************************************/

void InitializeRunControl(void)
{
        /*
        ** If the last power fail occurred while doing a non-SCC run then initiate a blank run
        **
        ** Note:  the "Power_Fail_Blank_Run" flag is only cleared at the completion of a
        **        blank run.  This guarantees its integrity through multiple powerfails
        **        before completing the blank run.
        */
        if ((Run_State == RUN_ACTIVE || Run_State == POST_RUN) && !Scc_Run)
        {
                Power_Fail_Blank_Run = TRUE;
        }

        Controlled_By_Host = FALSE;
        Start_Run_Pending = FALSE;
        Stop_Run_Pending = FALSE;
        Prepare_Seen_During_Post_Run = FALSE;
        Last_Run_Time = 0;
        SetHostReady(TRUE);  /* host readiness defaults to TRUE */
        RunIdle();  /* init everything else */
}

/***********************************************************************/
/*
 #####  #######    #    ####### #######  #####
#     #    #      # #      #    #       #     #
#          #     #   #     #    #       #
 #####     #    #     #    #    #####    #####
      #    #    #######    #    #             #
#     #    #    #     #    #    #       #     #
 #####     #    #     #    #    #######  #####
*/
/***********************************************************************/
/*
** FUNCTION: RunIdle
** ========
**
** PURPOSE:  This function performs the state transition to RUN_IDLE.
**
** RETURNED VALUE:  None.
*/
/***********************************************************************/

static void RunIdle ( void )
{
        INTERRUPTS_OFF

        /* clean up SCC in case of an abort */
        if (Scc_Run && (Run_State != POST_RUN))
        {
                StopBlankRun();
        }

        Run_State = RUN_IDLE;
        Pre_Run_Ready = FALSE;
        Run_Requested = FALSE;
        Blank_Run = FALSE;
        Scc_Run = FALSE;
        Run_Clock = 0;

//        TURN_PREP_RUN_LED_OFF();
//        TURN_POST_RUN_LED_OFF();

        if (SequenceInProgress())
        {
                /* blink run LED to indicate that unit is armed for an automated start */
//                BLINK_RUN_LED();
        }
        else
        {
//                TURN_RUN_LED_OFF();
        }

        if ( Auto_Prep_Run || Prepare_Seen_During_Post_Run )
        {
                Prepare(Auto);
                Prepare_Seen_During_Post_Run = FALSE;
        }

        /* force re-evaluation of readiness after Run_State change */
        ClearNotReadyEval();

        INTERRUPTS_ON
}

/***********************************************************************/
/*
** FUNCTION: PreRun
** ========
**
** PURPOSE:  This function performs the state transition to PRE_RUN.
**
** RETURNED VALUE:  None.
*/
/***********************************************************************/

static void PreRun( void )
{
        INTERRUPTS_OFF

        Run_State = PRE_RUN;
        Pre_Run_Ready = FALSE;
        Run_Clock = 0;

        AssertApgPrepare();

//        BLINK_PREP_RUN_LED();
//        TURN_POST_RUN_LED_OFF();

        if (SequenceInProgress() || GsvInjInProgress())
        {
                /* blink run LED to indicate that unit is armed for an automated start */
//                BLINK_RUN_LED();
        }
        else
        {
//                TURN_RUN_LED_OFF();
        }

        /* force re-evaluation of readiness after Run_State change */
        ClearNotReadyEval();

        INTERRUPTS_ON
}

/***********************************************************************/
/*
** FUNCTION: RunActive
** ========
**
** PURPOSE:  This function performs the state transition to RUN_ACTIVE.
*/
/***********************************************************************/

#pragma SECTION PROG=SysCode
static void RunActive( void )
{
       INTERRUPTS_OFF

       /* Clear the start run interrupt and leave it disabled.
       **
       ** WARNING:  the start run interrupt MUST be kept disabled whenever
       **           a start will be ignored (e.g. during runs.)  This prevents
       **           the continuous interrupts that would otherwise occur
       **           when the APG start line is held low.
       **
       ** This is redundant in most cases, as the start run interrupt service
       ** routine will have already disabled the interrupt.  This instance
       ** protects SCC runs and runs started by the GCRN datacomm cmd.
       */
       DISABLE_START_RUN_INTERRUPT

       /* notify the signal DSP so it can tag the data stream */
       /* the signal DSP samples this input at 16 kHz, so there must be at least 62.5 usec between transitions */
       SEND_START_TO_SIG_DSP

       /*
       ** If the run task interrupt is in the process of being serviced
       ** then we consider it already synchronized with this start run event.
       ** In the more common case where the run task is not currently being
       ** serviced then we sync the run task interrupt to this start run event.
       */
       if ( !Run_Task_Running )
       {
          /*
          ** Reset the pulse accumulator to prevent an interrupt from
          ** occuring while we play with the TFLG2 register below.
          */
          //*PACNT = 0;

          /*
          ** If a PAOVF interrupt is pending then the pulse accumulator
          ** is already set to the correct rollover value of 0.
          **
          ** If the more common case where the PAOVF interrupt is not pending
          ** then set the pulse accumulator to rollover at the next 8kHz pulse.
          */
          //if ( !(*TFLG2 & 0x20) )
          //{
          //   *PACNT = 0xff;
          //}
       }

       /* set semaphore to notify RunTask that a run has started */
       Start_Run_Pending = TRUE;

       /* set semaphore to notifiy sequencing that a run has occurred */
       Start_Has_Occurred = TRUE;

       INTERRUPTS_ON
}
#pragma SECTION UNDO

/***********************************************************************/
/*
** FUNCTION: PostRun
** ========
**
** PURPOSE:  This function performs the state transition to POST_RUN.
**
** RETURNED VALUE:  None.
*/
/***********************************************************************/

static void PostRun ( void )
{
        /* set semaphore to notify RunTask that a stop run has occurred */
        Stop_Run_Pending = TRUE;
}

/*

####### #     # ####### #     # #######  #####
#       #     # #       ##    #    #    #     #
#       #     # #       # #   #    #    #
#####   #     # #####   #  #  #    #     #####
#        #   #  #       #   # #    #          #
#         # #   #       #    ##    #    #     #
#######    #    ####### #     #    #     #####

*/

/***********************************************************************/
/*
** FUNCTION: Prepare
** ========
**
** PURPOSE:  Called to request entry into PRE_RUN state.
**
** RETURNED VALUE:  None.
*/
/***********************************************************************/

void Prepare( INVOCATION_MODE mode )
{
        INTERRUPTS_OFF
        /*
        ** If we are already in the first PRE_RUN substate and this request
        ** is from the keyboard, then advance to the second PRE_RUN substate.
        ** INET is ignorant of this substate change.
        */
        if ( (mode == Manual) && (Run_State == PRE_RUN) && !Pre_Run_Ready )
        {
                Pre_Run_Ready = TRUE;
//                TURN_PREP_RUN_LED_ON();
        }
        else if (Controlled_By_Host)
        {
#if 0
                /* notify MaVerick */
                DoMVKP();
#endif
                if ( Run_State == RUN_IDLE)
                {
                        PreRun();
                };
        }
        else
        {
                switch (Run_State)
                {
                case RUN_IDLE:
                        /* enter first PRE_RUN substate */
                        PreRun();
                        break;

                case POST_RUN:
                        /* record event for later action */
                        Prepare_Seen_During_Post_Run = TRUE;
                        break;
                }
        }
        INTERRUPTS_ON
}
/***********************************************************************/
/*
** FUNCTION:  StartRun
** ========
**
** PURPOSE:  Called to request the start of a gc run.
**
** RETURNED VALUE:  1 if accepted, 0 if rejected
**
** LIMITATIONS:
**
**   If Controlled_By_Host the request is passed to the MIO host,
**   otherwise an attempt to actually start a run is initiated.
**
**   StartRun will return to the caller a bit in D0 indicating that
**   start was accepted, (and the apg bus will strech start)
*/
/***********************************************************************/

#pragma SECTION PROG=SysCode
BIT8 StartRun( void )
{
        BIT8 success;

        /* ignore if already in a run or if startup is not complete */
        if ( (Run_State == RUN_ACTIVE) || (Power_On_Test_Complete == FALSE) )
        {
                success = 0;
        }
        else
        {
                success = 1;
                Run_Requested = TRUE;  /* reset after Run_State transitions to Run_Active */

                if (Controlled_By_Host)
                {
#if 0
                        if (!MVPending.bit.StartRun)
                        {
                                MVPending.bit.StartRun = 1;
                        }
#endif
                }
                else
                {
                        RunActive();
                }
        }

        return success;
}
#pragma SECTION UNDO
/***********************************************************************/
/*
** FUNCTION: StopRun
** ========
**
** PURPOSE:  This function is called to stop a gc run.
**
** RETURNED VALUE:  None.
*/
/***********************************************************************/

void StopRun( STOP_RUN_REASON reason )
{
        /*
        ** Always transmit any stop key press to APG bus, even when in INET
        ** mode and regardless of the current Run_State.  This way we can
        ** always stop all instruments hung on the APG bus, regardless
        ** of the system state.
        **
        ** Similarly, halt ALS on any stop key press.  Maverick should
        ** return an "abort" via the GCRN command following receipt of
        ** of an MVKS command, but we do it here as well just in case.
        ** This will result in a double APG Stop pulse, which is ugly but
        ** should not hurt anything.  And according to Tom P. there is
        ** no harm in setting Stop_Key_Pressed twice.
        */
        if (reason == Abort)
        {
                AssertApgStop();                /* signal stop to APG bus */
                Stop_Key_Pressed = TRUE;        /* signal sequence task to halt als */
        }

        /* if INET mode, just notify MaVerick */
        if (Controlled_By_Host)
        {
#if 0
                switch (reason)
                {
                case EndOvenRamp:
                        DoMVEN();  /* notify MaVerick of normal ENd of run event */
                        break;

                case EndPostTime:
                        DoMVPR();  /* notify MaVerick of normal end of Post Run */
                        break;

                case Abort:
                        DoMVKS();  /* notify MaVerick of stop "key press" */
                        break;
                }
#endif
        }
        /* if not INET mode, perform appropriate stop action */
        else
        {
                INTERRUPTS_OFF
                switch (Run_State)
                {
                case RUN_ACTIVE:
                        PostRun();
                        break;

                case PRE_RUN:
                case POST_RUN:
                        RunIdle();
                        break;
                }
                INTERRUPTS_ON
        }
}

/***********************************************************************/
/*
** FUNCTION: BlankRun
** ========
**
** PURPOSE:  Initiates a blank run.
**
** RETURNED VALUE: None.
*/
/***********************************************************************/

void BlankRun( void )
{
        PreRun();

        /* blink run LED to indicate that unit is armed for an automated start */
 //       BLINK_RUN_LED();

        /* set Blank_Run semaphore last, as it triggers call of StartRun() in RunTask() */
        Blank_Run = TRUE;
}

/***********************************************************************/
/*
** FUNCTION: SccPreRun
** ========
**
** PURPOSE: Begin PRE_RUN activities for SCC which will eventually call SccRun().
**
** RETURNED VALUE:  None.
*/
/***********************************************************************/

void SccPreRun ( void )
{
        PreRun();

        InitializeSccRun();

        /* blink run LED to indicate that unit is armed for an automated start */
 //       BLINK_RUN_LED();

        /* set Scc_Run semaphore last, as it triggers call of SccRun() in RunTask() */
        Scc_Run = TRUE;
}

/***********************************************************************/
/*
** FUNCTION: SccRun
** ========
**
** PURPOSE: Begin an SCC run.
**
** RETURNED VALUE:  None.
*/
/***********************************************************************/

void SccRun ( void )
{
     /* Notify Maverick of event */
     if (Controlled_By_Host)
     {
#if 0
          DoMVPL();
#endif
     }

     /* do a normal RunActive transition */
     RunActive();
}

/***********************************************************************/
/*
** FUNCTION: SetRunState
** ========
**
** PURPOSE:  The guts of the ChemComm GCRN command.
**
** RETURNED VALUE: UI_ERR == OK if successful
*/
/***********************************************************************/

UI_ERR SetRunState(INT32 state, INT16 abort)
{
        UI_ERR ui_err = OK;

#if 0
        /*
         * Clear ALL of the MVpending bits in response to any
         * host-initiated state change to insure that none are
         * ever accidentally left set.
         *
         * Maverick always sends a RunIdle transition after a
         * "loop up", so this resets our state after that event.
         */
        MVPending.word = 0;

#endif
        /*
        ** Recover after we sent a start run request to the host while the comm
        ** link or host was down:  if we are then told to do something other than
        ** start a run (note that INET controllers will tell us that via the
        ** MioStartRun function) we cancel the pending start run here.
        */
        if (Run_Requested && state != 2)
        {
                /* clear Run_Requested flag */
                Run_Requested = FALSE;

                /* re-enable the external (APG) start run interrupt which was
                   disabled by the start run interrupt service routine */
                ENABLE_START_RUN_INTERRUPT
        }

        /*
        ** This is just like StopRun(Abort), but the stop key press originated
        ** on some other INET device.
        */
        if (abort)
        {
                AssertApgStop();                /* signal stop to APG bus */
                Stop_Key_Pressed = TRUE;        /* signal sequence task to halt als */
        }

        INTERRUPTS_OFF
        switch (state)
        {
        case 0:
                /* this is always permitted, regardless of the Run_State */

                /* a run must always exit via PostRun() */
                if (Run_State == RUN_ACTIVE)
                {
                        PostRun();
                }

                RunIdle();
                break;
        case 1:
                if (Run_State == RUN_IDLE)
                {
                        PreRun();
                }
                else if (Run_State == POST_RUN)
                {
                        /* record event for later action */
                        Prepare_Seen_During_Post_Run = TRUE;
                }
                else
                {
                        ui_err = NOT_ALLOWED;
                }
                break;
        case 2:
                if (Run_State != RUN_ACTIVE)
                {
                        RunActive();
                }
                else
                {
                        ui_err = NOT_ALLOWED;
                }
                break;
        case 3:
                if (Run_State == RUN_ACTIVE)
                {
                        PostRun();
                }
                else
                {
                        ui_err = NOT_ALLOWED;
                }
                break;
        default:
                ui_err = (state < 0) ? PARAM_TOO_SMALL : PARAM_TOO_LARGE;
        }
        INTERRUPTS_ON

        return ui_err;
}
